package org.upc.edu.AIAExamples.communication;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.lang.acl.ACLMessage;

import java.util.ArrayList;

public class Casa extends Agent {
	
	long EAcumulada;
	ArrayList<Long> ultimsbalans;
	long mitjanabalans;
	FuncioGauss EProduida;
	FuncioGauss EConsumida;
	FuncioGauss Ingressos;
	long diners;
	int n;
	Parell[] info;
	final double PREU_CENTRAL = 1;
	
	private double milloroferta() {
		double millorPreu = info[0].y;
		for (Parell p:info){
			if (p.y < millorPreu) millorPreu = p.y;
		}
		return millorPreu;
	}

	private void send(String string, int senderid, long quantitat, double preu) {
		// depen del missatge (need,propose,accept) ha de enviar un missatge al senderid i actualitzar la matriu info
		ACLMessage msg = new ACLMessage();
		AID casaid = new AID();
        casaid.setLocalName("casa"+senderid);
		msg.addReceiver(casaid);
		msg.setContent(String.valueOf(quantitat)+ ", "+String.valueOf(preu));
		msg.setSender(this.getAID());
		if (string.equals("need")){
			msg.setPerformative(ACLMessage.REQUEST);	
		}
		else if (string.equals("propose")){
			msg.setPerformative(ACLMessage.PROPOSE);
		}
		else if (string.equals("accept")){
			msg.setPerformative(ACLMessage.CONFIRM);
		}
		else{
			System.out.println("Error funcio Send: la performativa "+string+" es incorrecta");
		}
		this.send(msg);
	}

	private Parell oferta(String content) {
		// TODO retorna un parell amb el valor de la oferta (QEnergia,Preu)
		String valors[] = content.split(",");
		Parell p = new Parell (Long.parseLong(valors[0]), Double.parseDouble(valors[1]));
		return p;
	}

	private int quinacasa(AID sender) {
		// TODO A partir del sender dona l'id de la casa: casa12 = 12
		String s = sender.getLocalName();
		int id = Integer.parseInt(s.substring(4));
		return id;
	}
	
	
	public class buyerbehaviour extends CyclicBehaviour{
		
		private long startime;
		
		public buyerbehaviour(Agent agent) {
			super(agent);
			ACLMessage buy = new ACLMessage(); /** les cases comencen per 0*/
            for (int i = 0; i < n ; ++i){
            	AID casai = new AID();
                casai.setLocalName("casa"+i);        	
    			buy.addReceiver(casai);
    			info[i] = new Parell();
            }
            buy.setPerformative(ACLMessage.REQUEST);
            buy.setContent(String.valueOf(-EAcumulada));
			buy.setSender(myAgent.getAID());
			myAgent.send(buy);
			startime = System.currentTimeMillis();
		}

		private int bestSeller() {
			int millor = -1;
			double cmax = -1;
			for (int i = 0; i < info.length; ++i) {
				double q = Math.abs((double) info[i].x/ (double) EAcumulada);
				double p = info[i].y/PREU_CENTRAL;
				double c = q * 0.3 + p * 0.7;
				if (c > cmax) {
					c = cmax;
					millor = i;
				}
			}
			return millor;
		}
		
		@Override
		public void action() {
			ACLMessage msg = receive();
			long dtime = System.currentTimeMillis()-startime;
			if(msg==null && dtime<=1000) return; //debug
			if(msg==null && dtime>1000) {
				int b = bestSeller();
	    		info[b] = oferta(msg.getContent());
	    		//negociacions per veure si confimem o fem contraoferta
	    		
				send("accept", b, info[b].x, info[b].y);
	    	}
			else if(msg.getSender().getLocalName().equals("clock")) {
				//? necessitem 2 clocks?
			}
			else{
				int senderid = quinacasa(msg.getSender());
				switch(msg.getPerformative()){
				case ACLMessage.PROPOSE:
					info[senderid] = oferta(msg.getContent());
					if(dtime>1000) {
						if(senderid == bestSeller()) send("accept",senderid, info[senderid].x, info[senderid].y);
						else send("propose",senderid, EAcumulada, milloroferta());
						/* abans contavem nomes el preu, ara contem segons la formula quantitat*.3+preu*0.7 ponderats sobre 1
						if(info[senderid].x>EAcumulada) send("propose",senderid, EAcumulada, milloroferta());
						else if(info[senderid].y<=milloroferta()) send("accept",senderid, info[senderid].x, info[senderid].y);
						else send("propose",senderid, EAcumulada, info[senderid].y);*/
					}
					break;
				case ACLMessage.CONFIRM:
					EAcumulada+=info[senderid].x;
					if(EAcumulada>=0) {
						// ? 2 clocks?
						casabehaviour b = new casabehaviour(myAgent);
						myAgent.addBehaviour(b);
						myAgent.removeBehaviour(this);
					}
					break;
				default:
					break;
				}
			}
			
		}
	}
	
	public class sellerbehaviour extends CyclicBehaviour{
		private long startime;
		
		public sellerbehaviour(Agent agent) {
			super(agent);
			for (int i = 0; i <= n ; ++i){ //les cases comencen en 0   
    			info[i] = new Parell();
            }
			startime = System.currentTimeMillis();
		}

		@Override
		public void action() {
			
			ACLMessage msg = receive();
			long dtime = System.currentTimeMillis()-startime;
			/*if(msg==null && dtime<=1000) return; //debug
			if(msg==null && dtime>1000) {
				//escullu a qui li intento vendre primer
			} */
			if (msg == null) return;
			else if(msg.getSender().getLocalName().equals("clock")) {
				casabehaviour b = new casabehaviour(myAgent);
				myAgent.addBehaviour(b);
				myAgent.removeBehaviour(this);
			}
			else{
				int senderid = quinacasa(msg.getSender());
				switch(msg.getPerformative()){
				case ACLMessage.REQUEST:
					info[senderid] = oferta(msg.getContent());
					if(info[senderid].x>EAcumulada) send("propose",senderid, EAcumulada, preuVenda(EAcumulada));
					else send("propose",senderid, info[senderid].x, preuVenda(info[senderid].x));
					break;
				case ACLMessage.PROPOSE:
					info[senderid] = oferta(msg.getContent());
					if (info[senderid].x<=EAcumulada) send("accept", senderid, info[senderid].x, info[senderid].y);
					else send("propose",senderid, EAcumulada, preuVenda(info[senderid].x));
					break;
				case ACLMessage.CONFIRM:
					if (EAcumulada >= info[senderid].x) send("accept", senderid, info[senderid].x, info[senderid].y);
					else send("propose",senderid, EAcumulada, preuVenda(EAcumulada));
					break;
				default:
					break;
				}
			}
		
			
		}

		private double preuVenda(long QEnergia) {
			// el preu unitari que li correspon per vendre aquesta Quantitat d'Energia
			double oferta = 0.8+(1-(double)QEnergia/(double)EAcumulada)*0.2; //sobre 1
			double pbase = 1/ Math.log(EAcumulada + Math.E);
			return pbase*PREU_CENTRAL*oferta;
		}
	}
	
	//pensem si sha de fer com un oneshot
	public class casabehaviour extends CyclicBehaviour
    {
		public casabehaviour(Agent agent) {
			super(agent);
		}

		public void action() {
			ACLMessage msg = receive();
			if(msg==null) return; //debug
			if(msg==null) {
				// si cal iniciem comerÃ§ entre cases
			}
			if(msg.getSender().getLocalName().equals("clock")) {
				if (EAcumulada < 0){
					System.out.println(myAgent.getLocalName()+": Energia negativa, enviant peticio de " + -EAcumulada);
					ACLMessage peticio = new ACLMessage();
					peticio.setSender(myAgent.getAID());
					AID central = new AID();
			        central.setLocalName("central");
					peticio.addReceiver(central);
					peticio.setContent(String.valueOf(-EAcumulada));
					//myAgent.send(peticio);
					EAcumulada = 0;
				}
				long balans = 0;
				balans += EProduida.next();
				balans -= EConsumida.next();
				if(ultimsbalans.size()>=10) ultimsbalans.remove(0);
				ultimsbalans.add(balans);
				EAcumulada += balans;
				mitjanabalans = mitjanabalans();
				diners += Ingressos.next();
				//negociacio
				
				if(EAcumulada < 0){
					sellerbehaviour b = new sellerbehaviour(myAgent);
					myAgent.addBehaviour(b);
				}
				if(EAcumulada > 0){
					buyerbehaviour b = new buyerbehaviour(myAgent);
					myAgent.addBehaviour(b);
				}
				
				System.out.println(myAgent.getLocalName()+": tick rebut, balanÃ§: "+balans+" Energia actual: "+EAcumulada);
				
			}
			else if(msg.getSender().getLocalName().equals("central")) {	
				EAcumulada += Long.parseLong(msg.getContent());
				diners -= Long.parseLong(msg.getContent()) * 8;
				System.out.println(myAgent.getLocalName()+": Energia rebuda: "+msg.getContent()+" Energia Actual: "+EAcumulada);
			}
			else {
				// peticions comerÃ§ entre cases
			}
		}
    }
	
	protected void setup() {
		Object[] args = getArguments();
		System.out.println("creant casa amb parametres: "+args[0].toString()+" "+args[1].toString()+" "+args[2].toString()+" "+args[3].toString());
		EConsumida = new FuncioGauss(Long.parseLong(args[0].toString()),Long.parseLong(args[1].toString()));
		EProduida = new FuncioGauss(Long.parseLong(args[2].toString()),Long.parseLong(args[3].toString()));
		EAcumulada = 0;
		ultimsbalans = new ArrayList<Long>();
		mitjanabalans = 0;
		Ingressos = new FuncioGauss(Long.parseLong(args[4].toString()),Long.parseLong(args[5].toString()));
		diners = 0;
		n = Integer.parseInt(args[6].toString());
		info = new Parell[n];
		/*ACLMessage peticio = new ACLMessage();
		peticio.setSender(this.getAID());
		AID central = new AID();
        central.setLocalName("central");
		peticio.addReceiver(central);
		peticio.setContent(args[0].toString());
		this.send(peticio);*/
		casabehaviour b = new casabehaviour(this);
		this.addBehaviour(b);
	}

	public long mitjanabalans() {
		long mitja = 0;
		for(long l: ultimsbalans) {
			mitja += l;
		}
		if (mitja > 0) mitja = 0;
		return -mitja/ultimsbalans.size();
	}
}
